package rest

import (
	"errors"
	"fmt"

	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

var LogPrefix = "REST"
var INVALID = errors.New("invalid request")
var NOT_FOUND = errors.New("resourse not found")

type BucketHandler interface {
	Get(id string) (io.Reader, int, error)
	Put(id string, body io.Reader) (int, error)
	Delete(id string) (int, error)
}

type BucketLister interface {
	List() (io.Reader, int, error)
	Post(body io.Reader) (string, int, error)
	CopyFrom(urlstr string) error
}

func ReadObject(req *http.Request, obj interface{}) error {

	body, err := ioutil.ReadAll(req.Body)

	if err != nil {
		return err
	}

	err = json.Unmarshal(body, obj)

	if err != nil {
		return err
	}

	return nil
}

func WriteObject(w http.ResponseWriter, req *http.Request, obj interface{}) error {

	text, err := json.Marshal(obj)

	if err != nil {
		return err
	}

	cb := req.FormValue("cb")

	if cb != "" {
		fmt.Fprintf(w, "%s(%s);", cb, string(text))
	} else {
		w.Write(text)
	}

	return nil
}

type bucketHandler struct {
	BucketHandler
}

type bucketLister struct {
	BucketLister
}

func HandleBucket(bucket BucketHandler) http.Handler {

	return Handler{bucketHandler{bucket}}
}

func ListBucket(bucket BucketLister) http.Handler {

	return Handler{bucketLister{bucket}}
}

func (h bucketHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {

	id := req.URL.Query().Get("id")

	if id == "" {
		return 400, errors.New("missing id query parameter")
	}

	switch req.Method {
	case "GET":
		resp, code, err := h.BucketHandler.Get(id)
		if err != nil {
			return code, err
		}
		io.Copy(w, resp)
		return code, nil
	case "PUT":
		return h.BucketHandler.Put(id, req.Body)
	case "DELETE":
		return h.BucketHandler.Delete(id)
	default:
		return 500, INVALID
	}

	return 0, nil
}

func (h bucketLister) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {

	switch req.Method {
	case "GET":
		reader, code, err := h.List()
		if err != nil {
			return code, err
		}
		io.Copy(w, reader)
		return code, nil
	case "POST":
		_, code, err := h.BucketLister.Post(req.Body)
		return code, err
	default:
		return 400, INVALID
	}

	return 0, nil
}

type HandlerFunc func(w http.ResponseWriter, req *http.Request) (int, error)

func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, req *http.Request) {

	log.Println(LogPrefix, req.RemoteAddr, req.Method, req.URL.String())

	code, err := h(w, req)

	if err != nil {
		log.Println("ERROR", req.RemoteAddr, req.Method, req.URL.String(), code, err)
		http.Error(w, err.Error(), code)
	} else {
		log.Println(req.RemoteAddr, code)
	}
}

type handler interface {
	ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error)
}

type Handler struct {
	handler
}

func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {

	log.Println(req.RemoteAddr, req.Method, req.URL.String())

	code, err := h.handler.ServeHTTP(w, req)

	if err != nil {
		log.Println("ERROR", req.RemoteAddr, req.Method, req.URL.String(), code, err)
		http.Error(w, err.Error(), code)
	}
}

func HandleFunc(pattern string, handler func(w http.ResponseWriter, req *http.Request) (int, error)) {

	http.Handle(pattern, HandlerFunc(handler))
}

type loggerHandler struct {
	http.Handler
}

func Log(handler http.Handler) http.Handler {

	return loggerHandler{handler}
}

func LogFunc(handler func(w http.ResponseWriter, req *http.Request)) http.Handler {

	return loggerHandler{http.HandlerFunc(handler)}
}

func (h loggerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {

	log.Println(LogPrefix, req.RemoteAddr, req.Method, req.URL.String())

	h.Handler.ServeHTTP(w, req)
}
